home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 52369 / 52369.xpi / content / betterfacebook.js next >
Text File  |  2010-01-23  |  59KB  |  1,117 lines

  1. // ==UserScript==
  2. // @name           Better Facebook
  3. // @namespace      http://userscripts.org/users/86416
  4. // @include        http://www.facebook.com/*
  5. // ==/UserScript==
  6.  
  7. /* TO DO?
  8. * Flag friends as "Best Friends" to appear in sidebar. Flag updates or wall activity, etc.
  9. */
  10.  
  11. // Options for this script
  12. var options = new GM_options('better_facebook');
  13. options.addOption( {name:'pin_notifications',type:'checkbox','default':true,description:'Pin the notifications panel to the upper right'} );
  14. options.addOption( {name:'show_friend_tracker',type:'checkbox','default':true,description:'Show Friend Tracker in the Sidebar'} );
  15. options.addOption( {name:'show_group_activity',type:'checkbox','default':true,description:'Show Group Activity in the Sidebar'} );
  16. options.addOption( {name:'hide_connect_box',type:'checkbox','default':false,description:'Hide the "Connect With Friends" box on the right'} );
  17. options.addHtml("<h2>Story Types</h2>");
  18. options.addFunction( function() {
  19.     var str = "";
  20.     for (var i in storyTypes) {
  21.         var val = options.get('story_types.'+i+'.display');
  22.         str += '<tr><td class="label">'+storyTypes[i]+'</td><td><select name="story_types.'+i+'.display"><option value="show" '+(val=='show'?'selected':'')+'>Show<option value="minimize" '+(val=='minimize'?'selected':'')+'>Minimize<option value="hide" '+(val=='hide'?'selected':'')+'>Hide</select></td></tr>';
  23.     }
  24.     return str;
  25. } );
  26.  
  27. // Hidden options
  28. options.addOption( {name:'highlight_cp',type:'hidden','default':true} );
  29. options.addOption( {name:'highlight_pin_notifications',type:'hidden','default':true} );
  30. options.addOption( {name:'highlight_friend_tracker',type:'hidden','default':true} );
  31. options.addOption( {name:'highlight_group_activity',type:'hidden','default':true} );
  32.  
  33.  
  34. // UTILITY functions
  35. // ==================================================================
  36. var DEBUG = false;
  37. function log(s) { 
  38.     if (DEBUG) {
  39.         console.log(s);
  40.     }
  41. }
  42. var _template = function(s) {
  43.     for (var i=1; i<arguments.length; i++) {
  44.         var arg = arguments[i];
  45.         if ("object"==typeof arg) {
  46.             for (var key in arg) {
  47.                 var val = arg[key];
  48.                 if (typeof val=='undefined') {val = '';}
  49.                 s = s.replace( new RegExp("%"+key+"%","g"),val);
  50.             }
  51.         }
  52.         else {
  53.             s = s.replace( new RegExp("%"+i+"%","g"),arg);
  54.         }
  55.     }
  56.     return s;
  57. }
  58. function $$(cn,func,context) {
  59.     context = context || document;
  60.     var els = context.getElementsByClassName(cn);
  61.     if (els && els.length) {
  62.         if (typeof func=="string") {
  63.             func = new Function(func);
  64.         }
  65.         for (var i=els.length-1; i>=0; i--) {
  66.             func.call(els[i]);
  67.         }
  68.     }
  69. }
  70. function setValue(key,val) {window.setTimeout( function() { GM_setValue(key,val); },0);}
  71. function getValue(key, def) {return GM_getValue(key,def);}
  72. function hasClass(o,re) {if (typeof re=="string") {re = new RegExp("(^|\\s)"+re+"(\\s|$)");}return (o.className && re.test(o.className));}
  73. function addClass(o,cn) {if (o.className==null || o.className=='') { o.className = cn; return;}if (hasClass(o,cn)) { return; }o.className = o.className + " " + cn; }
  74. function removeClass(o,re) { if (!hasClass(o,re)) { return; } if (typeof re=="string") { re = new RegExp("(^|\\s)"+re+"(\\s|$)"); } o.className = o.className.replace(re,' '); };
  75. function findParentByClass(o,re) { while (o=o.parentNode) { if (hasClass(o,re)) { return o; } } return null; }
  76. /* Trigger a function when an element with a certain ID is loaded */
  77. function onIdLoad(id,func,delay) {
  78.     delay = delay || 500;
  79.     var o = document.getElementById(id);
  80.     if (o==null) {
  81.         setTimeout(onIdLoad,delay,id,func,delay);
  82.     }
  83.     else {
  84.         func(o);
  85.     }
  86. }
  87. /* Trigger a function when something becomes non-null */
  88. function onDefined(obj,prop,func,delay) {
  89.     delay = delay || 500;
  90.     if (!obj[prop]) {
  91.         setTimeout(onDefined,delay,obj,prop,func,delay);
  92.     }
  93.     else {
  94.         func(obj[prop]);
  95.     }
  96. }
  97. function addGlobalStyle(css) {
  98.     var head, style;
  99.     head = document.getElementsByTagName('head')[0];
  100.     if (!head) { return; }
  101.  
  102.     // Allow the CSS block to be split by __section:
  103.     // Only display a given __section__ if there is a user pref called 'section' that is true-ish.
  104.     // The first section is always displayed.
  105.     var actualCss = "";
  106.     var sections = css.split('__');
  107.     if (sections) {
  108.         actualCss += sections[0];
  109.         sections.splice(1).forEach(function(str,i) {
  110.             var x = str.indexOf(':');
  111.             if (x>0) {
  112.                 var pref = str.substring(0,x);
  113.                 var val = str.substring(x+2);
  114.                 if (options && options.get(pref)) {
  115.                     actualCss += val;
  116.                 }
  117.             }
  118.         });
  119.     }
  120.  
  121.     style = document.createElement('style');
  122.     style.type = 'text/css';
  123.     style.innerHTML = actualCss;
  124.     head.appendChild(style);
  125. }
  126. // Create an element and attach class name, properties and events
  127. function el(type,cn,props,events,innerHTML) {
  128.     var o = document.createElement(type);
  129.     if (cn) { o.className=cn; }
  130.     if (props) {
  131.         for (var i in props) {
  132.             o[i] = props[i];
  133.         }
  134.     }
  135.     if (events) {
  136.         for (var i in events) {
  137.             o.addEventListener(i,events[i],false);
  138.         }
  139.     }
  140.     if (innerHTML) {
  141.         o.innerHTML = innerHTML;
  142.     }
  143.     return o;
  144. }
  145. function insertFirst(container,el) {
  146.     insertAtPosition(container,el,1);
  147. }
  148. function insertAtPosition(container,el,pos) {
  149.     if (container && container.childNodes && container.childNodes.length>pos-1) {
  150.         container.insertBefore(el, container.childNodes[pos-1]);
  151.     }
  152.     else {
  153.         container.appendChild(el);
  154.     }
  155. }
  156. // ==================================================================
  157.  
  158. // Check to see if we are on Live Feed
  159. var was_live = null;
  160. function isLiveFeed() {
  161.     var h = document.getElementsByClassName('UIIntentionalStream_LiveHeader');
  162.     var is_live = (h && h.length>0 && !hasClass(h[0],'hidden_elem'));
  163.     if (was_live==null) {
  164.         was_live = is_live;
  165.     }
  166.     else {
  167.         if (was_live!=is_live) {
  168.             is_live?show_cp():hide_cp();
  169.             was_live = is_live;
  170.         }
  171.     }
  172.     return is_live;
  173. }
  174.  
  175. var control_panel = null;
  176. function resolve_cp() {
  177.     if (control_panel==null) { 
  178.         control_panel = document.getElementById('better_fb_cp');
  179.     }
  180. }
  181. function show_cp() {
  182.     resolve_cp();
  183.     if (control_panel!=null) { control_panel.style.display="block"; }
  184. }
  185. function hide_cp() {
  186.     resolve_cp();
  187.     if (control_panel!=null) { control_panel.style.display="none"; }
  188. }
  189.  
  190. // CSS
  191. (function() {
  192. addGlobalStyle( (<><![CDATA[
  193.     .better_fb_cp { padding:5px 10px; border:1px solid #ccc; margin:3px 3px 8px 3px; }
  194.     .better_fb_cp legend { font-size:10px; color:#999; font-style:italic; }
  195.     
  196.     /* For when "show all" is clicked" */
  197.     .show_all > * { display:block !important; }
  198.     
  199.     /* When "Mark All Read" is clicked, everything should go away */
  200.     .all_read > * { display:none !important; }
  201.     
  202.     /* This css rule hides last read and all siblings after the latest read */
  203.     div.last_read, div.last_read~div.GenericStory { display:none; }
  204.     div.last_read~div.UIStory_new_comments { background-color:white; display:block; }
  205.  
  206.     /* Hide the new comments notification unless they are _after_ the last read */
  207.     div.GenericStory .better_fb_new_comment_notif { display:none; }
  208.     div.last_read~div.GenericStory .better_fb_new_comment_notif { display:block; }
  209.     
  210.     /* Handle the "delayed stream"  that Facebook injects */
  211.     div.last_read~#pagelet_delayed_stream div.GenericStory { display:none; }
  212.     div.last_read~#pagelet_delayed_stream div.UIStory_new_comments { background-color:white; display:block; }
  213.     div.last_read~#pagelet_delayed_stream div.GenericStory .better_fb_new_comment_notif { display:block; }
  214.     
  215.     div.UIStory_hidden_by_type { display:none; }
  216.     div.UIStory_hidden_by_fbid { display:none; }
  217.     
  218.     div.UIStory_minimized img.GenericStory_Pic { display:none; }
  219.     div.UIStory_minimized > *, div.UIStory_minimized { min-height:0px !important; padding-bottom:0px; }
  220.     div.UIStory_minimized .commentable_item, div.UIStory_minimized .GenericStory_Info { display: none; }
  221.     
  222.     .notifications_pushpin {position:fixed;top:16px;right:70px;z-index:5000;}
  223.     
  224.     .UIButton_better_fb { margin:0px 3px; }
  225.     
  226.     .better_fb_new_comment_notif {
  227.         background-color:gold !important;
  228.         -moz-border-radius: 5px;
  229.         font-size:11px;
  230.         line-height:14px;
  231.         padding:1px 5px !important;
  232.         color:black;
  233.         font-weight:bold;
  234.     }
  235.     .better_fb_stop_comments {
  236.         margin-left:10px;
  237.         text-decoration:underline;
  238.         padding-left: 18px;
  239.         background-image: url("data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%10%00%00%00%10%08%06%00%00%00%1F%F3%FFa%00%00%00%07tIME%07%D9%01%13%17%03%00%B4m%D0%B0%00%00%00%09pHYs%00%00%0B%13%00%00%0B%13%01%00%9A%9C%18%00%00%00%04gAMA%00%00%B1%8F%0B%FCa%05%00%00%02ZIDATx%DAu%93Mh%13A%14%C7%DF%EE%26%9B%CDW%B3iBR%08H%0B%D2%80%EE!%09%04%14%0F%96%24%1AAJ%C8%B9%08%15%15%15%F1%2C%F8AO%DE%0A9%A9%07%0F%A2%88%1ET%14%3C%D8CJz%14%14rP%10%25%A2P%A5%91%12%1Acb%926t%FC%BFe%82Q%D2%81%1F3%F3%E6%7D%CD%9B7D%23C%10i%60%09%BC%03%BF%80%90l%81*%B8D%7B%0D%1C%C6%C1%1Bi%C0s%09%2C%803%E0%0E%A8%CB%B3%15%10%1Bg%CC%117%C1%C9%3D%9C%EF%80%CFRo%03%84F%D3%E6%88%EB%20*8%06%91I%9C2%D66D%15v%90%23zp%0A%D7%90N%9E%0F%1D%2C%C9%D4%B2%AC%AC(%8A%09%AA%AA%AA%0Ai%CC%D7%105%A2%FB%90%2F%82%AD%3A%D1ei%B3%A0p%C1%E0%E7%2B%B6%F3%9A%A6%990%AC%80%04%3B%EFoos%26%B0%A5%5E%D0%E1%F0w%E0%00%EB%02%1C7w%06%03%24D%DFH%A6s%C5%E9t%0A%97%CB%25%3C%1E%8F%F0%F9%7C%F6%2C%8B'%AEk%DAc%C30%16A%D5%EDv%CFA%EF%0B%E4w%B9%164L%DF%EF%F7%8B%600(B%A1%90%08%87%C3%22%87%3D%9F%B5%88V%BD%5E%AF%7D%1D%D6%E1%99%03%C8%D7%11%AA%5D%88%A9)%17%BC%13%BC%13%BC%93%5B%D7%A9%D4%ED%F2I%EF%D1%F4%F4Md%C7k%B7%C3%E1%F8%3B%07%02%3AoX%D2F%0E%87YI%D7m%19%9Dn%B5%C8%1A%0C%F0%26%D1%E5%DB%13%13VD%D7%97S%A9%D49%04y%9F%CF%E7O%20%C8%3Au%BBG%A0Z'%BB%EBL%B3%82%D4%E2%E5r9%7D%3C%1E%FF%D4%C1%0B%D4P%13%D1h%1C%80%3C%04%F6%C7b%B1%9F%C5b%F1%A2eY%CF%12%89%C4%3D%14%EC%23l_%F1%15%9ER%B39G%86%B1%2F%9B%CDv_6%9B%1B%9E%DD%5D%BA%1A%89%10MN%FAp%DE%00%81%0B%18%C82%DAn%B7%8FU%0Cc%93%FA%FDY%C8%1F%FE%DBH%86a%BFyoff5%99L%D6%109'%9B%89%AB7%9B%C9d%9E%DC*%14JB%D3~%0BEy1%AE%95%07%A0-%D6%D6%E6apTB%92%83%22%9D%BE!T%B5%03~%0C%5BY%19q%C2%CD%B3%02%A2%E0-x-1%C0!%89%25u%CE%C2%F0%FB%B8%1F%C9%D7%B9%06%3E%8C%7Ce!%3F%12%7F%F1%F3%FF%DB%FC%01d%D1G%9Dc%B2%BB%D6%00%00%00%00IEND%AEB%60%82");
  240.         background-repeat:no-repeat;
  241.     }
  242.     div.GM_options_wrapper {
  243.         display:none;
  244.         position:absolute;
  245.         width:50%;
  246.         margin-left:25%;
  247.         margin-right:25%;
  248.         top:50px;
  249.  
  250.         background-color:white;
  251.         border:8px solid #3B5998;
  252.         -moz-border-radius: 15px;
  253.         padding:10px;
  254.  
  255.         z-index:5000;    
  256.         font-size:110%;
  257.     }
  258.     .GM_options_wrapper h2 {
  259.         font-size:16px;
  260.         background-color:#3B5998; /* #82B6D9;*/
  261.         color:white;
  262.         font-weight:bold;
  263.         padding:5px 10px;
  264.         margin:0px;
  265.     }
  266.     .GM_options_header {
  267.         background-color:white;
  268.         color:#3B5998;
  269.         min-height:90px;
  270.         border-bottom:5px solid #3B5998;
  271.     }
  272.     .GM_options_header a {
  273.         color:#3B5998;
  274.     }
  275.     .GM_options_message {
  276.         clear:both;
  277.         background-color:yellow;
  278.         padding:10px;
  279.         border:1px solid black;
  280.         -moz-border-radius: 10px;
  281.         margin:10px;
  282.         font-weight:bold;
  283.         font-size:larger;
  284.     }
  285.     .GM_options_body {
  286.         height:400px;
  287.         overflow:auto;
  288.     }
  289.     .GM_options_wrapper table.GM_options {
  290.         border:1px solid black;
  291.     }
  292.     .GM_options_wrapper td.label, .GM_options_wrapper td.input, .GM_options_wrapper td.html  {
  293.         border:1px solid #aaa;
  294.         margin:5px;
  295.         padding:3px;
  296.     }
  297.     .GM_options_buttons {
  298.         float:right;
  299.         width:200px;
  300.         height:50px;
  301.         vertical-align:middle;
  302.         text-align:center;
  303.     }
  304.     .GM_options_buttons input {
  305.         background-color:#3B5998;
  306.         color:white;
  307.         font-size:16px;
  308.         font-weight:bold;
  309.     }
  310.  
  311.     /* Messages, Tips */
  312.     .better_fb_message {
  313.         clear:both;
  314.         background-image:url("data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%23%00%00%00%23%08%02%00%00%00%91%BB%24%0E%00%00%00%06tRNS%00%00%00%00%00%00n%A6%07%91%00%00%00%09pHYs%00%00%0B%13%00%00%0B%13%01%00%9A%9C%18%00%00%06.IDATx%DA%E5%97%5Dl%DB%D6%15%C7%FF%A4%24J%B2d%C9%96%AA%C5%8A%12%3B%8E%5D%E7s%C1%92eu%8B%A4S%8B%C6Y%DD%06%CD%87%8B%3C%04%5B%83%3E5%D8V%EC%E3%A1%F0C%91%01%1D0l%E8%B0%97%14%E9%8A%AC%40%1E%16%17k%96%B5%CD%86%B9M%D6%C0%CE%9C%CD%AE%5D'UmGql%CB%B2%ADY%1F%96%25J%A4H%5E%92%F7%EEAN%90%D5%5Em%A7%C1%5E%F6%7F%22.%0F%CF%8F%FF%83s%EF!%81%FF%95%B8%95%87%8E%0Fv%C5%23%03%F3%B3%09R%92%84%0A%B7%CB%5B%B9%A6~%7B%C3%EE%C7%BD%81%E0%83!%A9R%F1%FAG%E7%BA%3B%CE%24%A2%11%00N%B7%DDQa'%84W%8A%25j%12O%20%B4%F3%3B%87%F7%1E%FBAM%5D%E3W%22M%0D%F5w%FC%ECD%22%1A%F1%04B%E1%A3%5B%EAv%B4%F8BM%8E%0A%AF%AE%5Bsi-%1D%8B%96%DF%C0%13%08%1Dx%B9%FD%B1%B6%13%F7Y%D9%A1%AE%3F%B7%EF%09%BC%D2%EC%EE%3C%7DRM%7F%C8%A8%C8%BE%20*S%7D%F8%9F%17~%FB%FAa%DF%8Fv%B8%3AO%9F%BC%1F%CCd%A4%B7%7DO%E0%E4%93%FE%5B%3D%BF%A1%FA03%A6%18%15%19%95%17%C1DF%C5%5C%E2%FC%A9%176%BC%BC%CDv%E5%EC%AFW%87Q%A5%E2%2F%DB%BE%F9J%B3%FBVw%3BcW%A8%DA%C7%CC4%A32c%84-!%C2%A8H%E6%DEx%FD%B0%AF%7DO%602%D2%BBdN~%C9%D5%7F%FC%F1%ADD4%B2%EF%F8%E3%0F7%D71%0D%9C%B5%DC%5D%06%00%40%FFb4%D3%01%D8%7C%DF%7B%FE%C7%B5J%B1%F4%D73%BFX)I%16%E7%BB%3B%DE%AEm%AA%7Cl_%08%E6%3A%C0%C5%18%01S%EF%26%BD%03%BB%07%C94%00u%CD%2F%3D%F247%DA%D35%3E%D8%B5%22R%EC%FA51%93%D8%FC%A8%C7%B5%A6%1A%00%A0%01%1Ac%04L%03%8CE%B0%3B%3C*r%F6M%FB%9F%A9d%A6y%FD%A3%0F%16%A7%B5.%5E%9A%1E%E9%07%B0%F9%EB%B5%8CV%82%8A%1C%00%D8a%C1%82-%0E%60%E5%0Db%BB%E3%D2(%DFb%1A%7C%5B%D6z%FDdl%E0%EA%8A%3C%E5fg%3DU%B6%AAj%C1%24%C4%D4%F2%8C%8A%80%0C%AA-%D4%B0%EC%0C%06%98%B2%E0%8F%8A%8C%11%C6%8A0e%00k%B7%98J%B1%24ff%97%F7%94%CF%A7%EET_%E4%E1%06%AA%60%CA%B0XA%C1x%CF%7Fnu%0D%00c%C4%24%BC%C5%22%19J%DC%02T%BA%8DIyN)%C9%CB%93%9C%82%BB%7CA%F23B%B5%97%D7f%60%07%07%C0%02P0%DE%0EF8N%00%E7%00g%07%E7%A4%B4%1A%98%26%85%9C%60%8FC%89%14%25%97%C5%E6rV%B8%96%AF%5E%E5Ck%D4%92%96%CF%11%00T%8DQSZ%A8%A1)%82%E5aJ%00%C0%FB%C1%07%00%2Bh%9E%91iUJ%F0%FC%18S%AFh%12W%9C%E5%2B%FD~%AB%60_%DES%A8i%93%A6%B0%B9X%3A%B8%D1o(%25%2Bb%BC%03L%93%60%07g%0Dq%B6%06%00%A0Yf%0C%C1LS%26%98%25%86%92%88%8A%24G%22b%9E%CF%A6-%B5%BBB.%AFoyR%FD%EE%7D%CE%CA%8A%81%3E%B5qW%D1%EAp%00%25%A6%25y%A1%CE%EE%080%3D%C3%1B%09F%C5%BB%C1T%CB%EBr%80%EA%19*%9D%85%80%DB%83%D5J%B1%B8%F3%89gp%EA%E2%F2%D5%AB%A9k%DC%F4h%CB%F8%E7%F3c%833%86%AA%EA%8An%C8I%AD%A0%15%26%FE%AE%CD%7DbH%7D0%26%60L%98%A5%A8)%F7%E9R%8E%EA%19*%7F%C8HZ%93%B8%1B%7D%8E%AA%9A%DA%AD%DF%3E%B8%A2%FD%04%E0%A9%17%7Fr%B3%E7b%CF%DF%D2%EBjy%C1%E9%B4%BA(%EF%00%91*L%E5!%00%8Ejb%B3%2B%0B%9E%88%22O%8D%D8%5D%098%F0%AF%09%F7%F4%AD%B9-%7B%9F%5Br6.%7D%EE%D5n%FF%D6%93%C7%7F%3A3%AE%BD%7F%BED%14E%2F%E8%24%3Bk%B5%DE%06%1D%D5%8B%B9%E2%94%AC%CC%8B%864D%B2%97%D5%EC%A4%C5n%02%D0%95*1c'%84%AFih%5C%C5%09%0B%A0%F5%FB%AF5%1F%3C6%FAi%E2%FD%F3%A5B%862%BD%A8%CE%17uq%9Ci%FDR%F2%B3%B9%1BS%E9%D1%92V%B0%94%83%0D%A9%A4%15%2C%B2%CC%98i%DA%2B%3C%AB%23%018%F6%F33%E1%EF%FEp%ECF%E6%F7o%A7%AEu%99rN%E0%99%0D%80%D5%A53%9BA%92%AE%DCL55%7C%00%88j1%95J%2B%AA%BE%24%9B%E5%CB%07%D5%BB%1F%F7%BEy%F6%DC%D4%CD%E1%E1%BE%C9%A1%88nR%A1%AE%C1i%B5p%16%81%A3%84S%E7%AC%BCi%DA%BC.%0BW%D4%09-%88l%F8%86l%04%B7uv%F5%AC%B4%23%EE%D5%AE%D6%A3%00%06%3B%DF%BD%F4%BB_%5D%ED%8C%025%7B%5B%AA%04%1B%84%F50U%B3%98%CC%DB%D7%04e%E2%8A%8Ed%13%F1%FC%07b%20%3C%99%5E2%8Fu%85SxW%EBQ13%7B%EE%B5%97%AEv~%2C%F8%CCmM%B6%E1Q%3D%11%CF%EF%0D%85%B4%D4lLtM%CF%ECp%EC%3E%B4%7D%EABwww2%3E%B6%F8S%C9%BA%F2%91%EF%0D%04%C5%CC%EC%9B'%0E%5C%BB8%F5%07w%AE%BC%A8K%F2%E4%CD%60%D6%16%DC%DF%D2r%E8%E0s%C9Tjh%24z%E1%2F%97%EE%DF%D3%5DXG%C7%3Bo%BD%FA%EA%F6%AD%8Fl%DA%BC%15%40%CA%E9%0AVy%B7U%7BUU%B95z%7BC%DD%06%00%FD%03%03%AB%EB%BD%25%F5l%CB%13%A1u%EBs%85%85%A1%A0*r%3E%2F%26S%A9d*%95H%24%3C%1E%CF%7F%7Bp%D5%24o%20%D8%D8%B0113%0D%C0%E7%F7%F9%FC%BE%60%CD%D7%BC%1E%AF%208%E2%F1xo_%3F%80%A6%A6%87%1F%00%09%C0%FE%96%16%00%BD%9F%F4%CFg%E7%E7%B3%F3%B1X%2C%16%8B%01p%3A%1D%9F%5D%EF%07%D0%FAt%EB%83!%B5%1D9%14%0E%87%133%D3s%D9%AC%CF%EF%AB%09%AE%AD%AF%AF'D%7D%EFO%17%86F%A2mG%8E%EC%DC%F9%8D%AF%F4%AFq%AF%E6%B2%D9%17%8F%BF04%12%05%10Z%B7%BE%5CL%00%E1p%F8%F4%1B%A7*%5C%AE%07F*%AB%A3%E3%9DK%97%2F%8F%8DO%00hl%D8%F8%7C%5B%DB%81%03%CF%E2%FFH%FF%06%DB%01b%5BR%10nj%00%00%00%00IEND%AEB%60%82");
  315.         min-height:40px;
  316.         padding-bottom:5px;
  317.         background-repeat:no-repeat;
  318.         background-position:2px 3px;
  319.         background-color:#FFFDEA;
  320.         padding-left: 55px;
  321.         padding-top:2px;
  322.         font-family:arial;
  323.         font-weight:bold;
  324.         font-size:12px;
  325.         margin:3px;
  326.         border:3px solid #F4D307;
  327.         -moz-border-radius:8px;
  328.     }
  329.     .better_fb_close {
  330.         width:50px;
  331.         background-color:#8B8C8D;
  332.         color:white;
  333.         border:1px solid #999;
  334.         border-color:#ddd #999 #999 #ddd;
  335.         text-align:center;
  336.         cursor:pointer;
  337.         margin:5px 0px;
  338.     }
  339.     
  340.     .trace {
  341.         border:1px solid #ccc;
  342.         color:#999;
  343.         clear:both;
  344.     }
  345.  
  346.     .bf_group_update {
  347.         background:none repeat scroll 0 0 #FFF8CC;
  348.         border-bottom:1px solid #FFE222;
  349.         color:#000000;
  350.         padding:0 0 1px;
  351.         font-size:11px;
  352.         margin:1px 0px 10px 5px;
  353.     }
  354.     .bf_group_h4 {
  355.         margin-top:7px;
  356.     }
  357.     
  358. __hide_connect_box:
  359.     #pagelet_connectbox { display:none; }
  360.  
  361. ]]></>).toString());
  362. })();
  363.  
  364. function hideStory(o) {
  365.     var story = findParentByClass(o,classUIStory);
  366.     if (story!=null && story.style) {
  367.         story.style.display='none';
  368.     }
  369. }
  370.  
  371. var classUIStory = /(^|\s)UIStory(\s|$)/;
  372. var classUISelectList = /(^|\s)UISelectList(\s|$)/;
  373.  
  374. var storyTypes = {
  375.  '8': 'New friend notifications',
  376.  '21': 'Friends joining groups',
  377.  '38': 'Events friends are attending',
  378.  '46': 'Status updates',
  379.  '65': 'Friends tagged in pictures',
  380.  '80': 'Links',
  381.  '94': 'New events',
  382.  '128': 'Fan page wall posts',
  383.  '161': 'Friends becoming fans of pages',
  384.  '236': 'Notes posted by fan pages',
  385.  '237': 'Posts from apps like Tweedeck, iPhone',
  386.  '247': 'Photo albums',
  387.  '259': 'New profile pictures',
  388.  '263': 'Video links'
  389. }
  390.  
  391. function getDataProperty(o,prop,container) {
  392.     var data = o.getAttribute(container);
  393.     if (data) {
  394.         var attrs = JSON.parse(data);
  395.         if (attrs && attrs[prop]) {
  396.             return attrs[prop];
  397.         }
  398.     }
  399.     return null;
  400.  
  401. }
  402. function getStoryProperty(o,prop) { return getDataProperty(o,prop,'data-ft'); }
  403. function getStoryTypeId(o) { return getStoryProperty(o,'sty') || -1; }
  404.  
  405. function getFirstElementByClassName(container,cn) {
  406.     var list = container.getElementsByClassName(cn);
  407.     if (list && list.length>0) {
  408.         return list[0];
  409.     }
  410.     return null;
  411. }
  412.  
  413. function isStoryTypeHidden(id) { return "hide"==options.get('story_types.'+id+'.display'); }
  414. function isStoryTypeMinimized(id) { return "minimize"==options.get('story_types.'+id+'.display'); }
  415.  
  416. function hideNewComments(fbid) {
  417.     options.set('story_data.'+fbid+'.no_comments',true);
  418.     options.save();
  419. }
  420.  
  421. function getCommentsTargetFbid(o) {
  422.     // Get the target_fbid the old way
  423.     if (o && o.getElementsByClassName) {
  424.         var comments = o.getElementsByClassName('commentable_item');
  425.         if (comments && comments.length>0) {
  426.             var c = comments[0];
  427.             // This is now a FORM object with a hidden input
  428.             if (c.elements && c.elements[2]) {
  429.                 var data = JSON.parse(c.elements[2].value);
  430.                 return data.target_fbid;
  431.             }
  432.         }
  433.     }
  434. }
  435. function time() {
  436.     return +new Date;
  437. }
  438. function fixStory(o) {
  439.     if (hasClass(o,classUIStory)) {
  440.         var traceBuffer = "";
  441.         var trace = function(str) {
  442.             traceBuffer+=str+". ";
  443.         }
  444.         
  445.         var type = getStoryTypeId(o);
  446.  
  447.         // MINIMIZE STORIES BY TYPE
  448.         if (isStoryTypeMinimized(type)) {
  449.             addClass(o,"UIStory_minimized");
  450.             if (DEBUG) { trace("Minimized by type"); }
  451.         }
  452.         // HIDE STORIES BY TYPE
  453.         if (isStoryTypeHidden(type)) {
  454.             addClass(o,"UIStory_hidden_by_type");
  455.             if (DEBUG) { trace("Hidden by type"); }
  456.         }
  457.         
  458.         // HIDE OLD STORIES
  459.         // ----------------
  460.         // Facebook removed pub_time from the source, so we need to use fbid instead, and just hide stories once we've seen the latest fbid.
  461.         // We have to make the assumption that stories will get inserted in order with the most recent at the top.
  462.         var fbid = getStoryProperty(o,'fbid');
  463.         if (DEBUG) { trace("fbid="+fbid); }
  464.     
  465.         if (o && o.getElementsByClassName) {
  466.             var comments = o.getElementsByClassName('feed_comments');
  467.         }
  468.         var c = null;
  469.         var count = 0;
  470.         if (comments && comments.length>0) {
  471.             c = comments[0];
  472.             if ( /view all (\d+) comments/i.test(c.innerHTML) ) {
  473.                 count = +RegExp.$1;
  474.             }
  475.             else {
  476.                 count = c.childNodes.length || 0;
  477.             }
  478.             if (DEBUG) { trace(count+" comments"); }
  479.         }
  480.         // Adding this class will automatically hide next siblings
  481.         if (fbid == last_seen_fbid) {
  482.             addClass(o,"last_read");
  483.             if (DEBUG) { trace("This post was the last marked as read"); }
  484.             
  485.             // The last read may be in the delayed stream, so mark the whole stream as last_read also so CSS still works
  486.             var pp = o.parentNode.parentNode;
  487.             if (pp && pp.id=="pagelet_delayed_stream") {
  488.                 addClass(pp,"last_read");
  489.                 if (DEBUG) { trace("post is in the delayed stream!"); }
  490.             }
  491.         }
  492.  
  493.         // Check to see if it has new comments
  494.         // But not if the user has disabled following of comments
  495.         if (fbid && options.get('story_data.'+fbid+'.no_comments')) {
  496.             // do nothing
  497.             if (DEBUG) { trace("Comments hidden for post"); }
  498.         }
  499.         else {
  500.             var target_fbid = getCommentsTargetFbid(o);
  501.             var stored_count_obj = options.get('story_data.'+target_fbid+'.cc');
  502.             var t = time();
  503.             
  504.             var stored_count = 0;
  505.             if (stored_count_obj && typeof(stored_count_obj.c)!="undefined") {
  506.                 if (DEBUG) { trace("Object is stored:" + stored_count_obj.c); }
  507.                 stored_count = stored_count_obj.c
  508.             }
  509.             else {
  510.                 stored_count_obj = {'c':0, 't':t};
  511.             }
  512.             
  513.             if (DEBUG) { trace("Stored comment count="+stored_count); }
  514.             if (count > stored_count) {
  515.                 addClass(o,"UIStory_new_comments");
  516.                 // Add a new box into the comment area
  517.                 if (c!=null) {
  518.                     var new_count = count-stored_count;
  519.                     var text = new_count+" new comment"+((new_count>1)?"s":"");
  520.                     var section = el('div','ufi_section better_fb_new_comment_notif',{'innerHTML':text});
  521.                     var a = el('a','better_fb_stop_comments',{'innerHTML':'mute comment alerts on this post'},{'click':function() { hideNewComments(fbid); hideStory(this); return false; } } );
  522.                     section.appendChild(a);
  523.                     
  524.                     if (!c.childNodes || c.childNodes.length==0) {
  525.                         c.appendChild(section);
  526.                     }
  527.                     else {
  528.                         c.insertBefore(section, c.childNodes[0]);
  529.                     }
  530.                 }
  531.                 stored_count_obj.t = t;
  532.                 stored_count_obj.c = count;
  533.             }
  534.             options.set('story_data.'+target_fbid+'.cc',stored_count_obj);
  535.         }
  536.         
  537.         // Append the trace
  538.         if (DEBUG && traceBuffer) {
  539.             o.appendChild( el('div',"trace",null,null,traceBuffer) );
  540.         }
  541.     }
  542. }
  543.  
  544. function fixStories(o) {
  545.     if (o && o.length) {
  546.         for (var i=0; i<o.length; i++) {
  547.             fixStory(o[i]);
  548.         }
  549.     }
  550. }
  551.  
  552. function handleDomInsertion(e) {
  553.     var o = e.target;
  554.     
  555.     if (isLiveFeed()) {
  556.         if (hasClass(o,/(^|\s)UIIntentionalStream_Content(\s|$)/)) {
  557.     //        log("Inserted: UIIntentionalStream_Content");
  558.             fixStories(o.getElementsByClassName('GenericStory'));
  559.         }
  560.         else if (o && o.getElementsByClassName) {
  561.             var stream = o.getElementsByClassName('GenericStory');
  562.             if (stream && stream.length) {
  563.                 fixStories(stream);
  564.             }
  565.             else {
  566.                 fixStory(o);
  567.             }
  568.         }
  569.     }
  570. }
  571.  
  572. // PIN NOTIFICATIONS
  573. if (options && options.get('pin_notifications')) {
  574.     onIdLoad('presence_notifications',function(notifications) {
  575.         notifications.style.position="fixed";
  576.         notifications.style.top="35px";
  577.         notifications.style.display="block";
  578.         
  579.         var img = el('img','notifications_pushpin',{src:"data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%24%00%00%00%20%08%06%00%00%00z%91%DA%8E%00%00%00%09pHYs%00%00%0B%13%00%00%0B%13%01%00%9A%9C%18%00%00%0AMiCCPPhotoshop%20ICC%20profile%00%00x%DA%9DSwX%93%F7%16%3E%DF%F7e%0FVB%D8%F0%B1%97l%81%00%22%23%AC%08%C8%10Y%A2%10%92%00a%84%10%12%40%C5%85%88%0AV%14%15%11%9CHU%C4%82%D5%0AH%9D%88%E2%A0(%B8gA%8A%88Z%8BU%5C8%EE%1F%DC%A7%B5%7Dz%EF%ED%ED%FB%D7%FB%BC%E7%9C%E7%FC%CEy%CF%0F%80%11%12%26%91%E6%A2j%009R%85%3C%3A%D8%1F%8FOH%C4%C9%BD%80%02%15H%E0%04%20%10%E6%CB%C2g%05%C5%00%00%F0%03yx~t%B0%3F%FC%01%AFo%00%02%00p%D5.%24%12%C7%E1%FF%83%BAP%26W%00%20%91%00%E0%22%12%E7%0B%01%90R%00%C8.T%C8%14%00%C8%18%00%B0S%B3d%0A%00%94%00%00ly%7CB%22%00%AA%0D%00%EC%F4I%3E%05%00%D8%A9%93%DC%17%00%D8%A2%1C%A9%08%00%8D%01%00%99(G%24%02%40%BB%00%60U%81R%2C%02%C0%C2%00%A0%AC%40%22.%04%C0%AE%01%80Y%B62G%02%80%BD%05%00v%8EX%90%0F%40%60%00%80%99B%2C%CC%00%208%02%00C%1E%13%CD%03%20L%03%A00%D2%BF%E0%A9_p%85%B8H%01%00%C0%CB%95%CD%97K%D23%14%B8%95%D0%1Aw%F2%F0%E0%E2!%E2%C2l%B1Ba%17)%10f%09%E4%22%9C%97%9B%23%13H%E7%03L%CE%0C%00%00%1A%F9%D1%C1%FE8%3F%90%E7%E6%E4%E1%E6f%E7l%EF%F4%C5%A2%FEk%F0o%22%3E!%F1%DF%FE%BC%8C%02%04%00%10N%CF%EF%DA_%E5%E5%D6%03p%C7%01%B0u%BFk%A9%5B%00%DAV%00h%DF%F9%5D3%DB%09%A0Z%0A%D0z%F9%8By8%FC%40%1E%9E%A1P%C8%3C%1D%1C%0A%0B%0B%ED%25b%A1%BD0%E3%8B%3E%FF3%E1o%E0%8B~%F6%FC%40%1E%FE%DBz%F0%00q%9A%40%99%AD%C0%A3%83%FDqanv%AER%8E%E7%CB%04B1n%F7%E7%23%FE%C7%85%7F%FD%8E)%D1%E24%B1%5C%2C%15%8A%F1X%89%B8P%22M%C7y%B9R%91D!%C9%95%E2%12%E9%7F2%F1%1F%96%FD%09%93w%0D%00%AC%86O%C0N%B6%07%B5%CBl%C0~%EE%01%02%8B%0EX%D2v%00%40~%F3-%8C%1A%0B%91%00%10g42y%F7%00%00%93%BF%F9%8F%40%2B%01%00%CD%97%A4%E3%00%00%BC%E8%18%5C%A8%94%17L%C6%08%00%00D%A0%81*%B0A%07%0C%C1%14%AC%C0%0E%9C%C1%1D%BC%C0%17%02a%06D%40%0C%24%C0%3C%10B%06%E4%80%1C%0A%A1%18%96A%19T%C0%3A%D8%04%B5%B0%03%1A%A0%11%9A%E1%10%B4%C118%0D%E7%E0%12%5C%81%EBp%17%06%60%18%9E%C2%18%BC%86%09%04A%C8%08%13a!%3A%88%11b%8E%D8%22%CE%08%17%99%8E%04%22aH4%92%80%A4%20%E9%88%14Q%22%C5%C8r%A4%02%A9Bj%91%5DH%23%F2-r%149%8D%5C%40%FA%90%DB%C8%202%8A%FC%8A%BCG1%94%81%B2Q%03%D4%02u%40%B9%A8%1F%1A%8A%C6%A0s%D1t4%0F%5D%80%96%A2k%D1%1A%B4%1E%3D%80%B6%A2%A7%D1K%E8ut%00%7D%8A%8Ec%80%D11%0Ef%8C%D9a%5C%8C%87E%60%89X%1A%26%C7%16c%E5X5V%8F5c%1DX7v%15%1B%C0%9Ea%EF%08%24%02%8B%80%13%EC%08%5E%84%10%C2l%82%90%90GXLXC%A8%25%EC%23%B4%12%BA%08W%09%83%841%C2'%22%93%A8O%B4%25z%12%F9%C4xb%3A%B1%90XF%AC%26%EE!%1E!%9E%25%5E'%0E%13_%93H%24%0E%C9%92%E4N%0A!%25%902I%0BIkH%DBH-%A4S%A4%3E%D2%10i%9CL%26%EB%90m%C9%DE%E4%08%B2%80%AC%20%97%91%B7%90%0F%90O%92%FB%C9%C3%E4%B7%14%3A%C5%88%E2L%09%A2%24R%A4%94%12J5e%3F%E5%04%A5%9F2B%99%A0%AAQ%CD%A9%9E%D4%08%AA%88%3A%9FZIm%A0vP%2FS%87%A9%134u%9A%25%CD%9B%16C%CB%A4-%A3%D5%D0%9Aigi%F7h%2F%E9t%BA%09%DD%83%1EE%97%D0%97%D2k%E8%07%E9%E7%E9%83%F4w%0C%0D%86%0D%83%C7Hb(%19k%19%7B%19%A7%18%B7%19%2F%99L%A6%05%D3%97%99%C8T0%D72%1B%99g%98%0F%98oUX*%F6*%7C%15%91%CA%12%95%3A%95V%95~%95%E7%AATUsU%3F%D5y%AA%0BT%ABU%0F%AB%5EV%7D%A6FU%B3P%E3%A9%09%D4%16%AB%D5%A9%1DU%BB%A96%AE%CERwR%8FP%CFQ_%A3%BE_%FD%82%FAc%0D%B2%86%85F%A0%86H%A3Tc%B7%C6%19%8D!%16%C62e%F1XB%D6rV%03%EB%2Ck%98Mb%5B%B2%F9%ECLv%05%FB%1Bv%2F%7BLSCs%AAf%ACf%91f%9D%E6q%CD%01%0E%C6%B1%E0%F09%D9%9CJ%CE!%CE%0D%CE%7B-%03-%3F-%B1%D6j%ADf%AD~%AD7%DAz%DA%BE%DAb%EDr%ED%16%ED%EB%DA%EFup%9D%40%9D%2C%9D%F5%3Am%3A%F7u%09%BA6%BAQ%BA%85%BA%DBu%CF%EA%3E%D3c%EBy%E9%09%F5%CA%F5%0E%E9%DD%D1G%F5m%F4%A3%F5%17%EA%EF%D6%EF%D1%1F704%086%90%19l18c%F0%CC%90c%E8k%98i%B8%D1%F0%84%E1%A8%11%CBh%BA%91%C4h%A3%D1I%A3'%B8%26%EE%87g%E35x%17%3Ef%ACo%1Cb%AC4%DEe%DCk%3Cabi2%DB%A4%C4%A4%C5%E4%BE)%CD%94k%9Af%BA%D1%B4%D3t%CC%CC%C8%2C%DC%AC%D8%AC%C9%EC%8E9%D5%9Ck%9Ea%BE%D9%BC%DB%FC%8D%85%A5E%9C%C5J%8B6%8B%C7%96%DA%96%7C%CB%05%96M%96%F7%AC%98V%3EVyV%F5V%D7%ACI%D6%5C%EB%2C%EBm%D6WlP%1BW%9B%0C%9B%3A%9B%CB%B6%A8%AD%9B%AD%C4v%9Bm%DF%14%E2%14%8F)%D2)%F5Sn%DA1%EC%FC%EC%0A%EC%9A%EC%06%ED9%F6a%F6%25%F6m%F6%CF%1D%CC%1C%12%1D%D6%3Bt%3B%7Crtu%CCvlp%BC%EB%A4%E14%C3%A9%C4%A9%C3%E9Wg%1Bg%A1s%9D%F35%17%A6K%90%CB%12%97v%97%17Sm%A7%8A%A7n%9Fz%CB%95%E5%1A%EE%BA%D2%B5%D3%F5%A3%9B%BB%9B%DC%AD%D9m%D4%DD%CC%3D%C5%7D%AB%FBM.%9B%1B%C9%5D%C3%3D%EFA%F4%F0%F7X%E2q%CC%E3%9D%A7%9B%A7%C2%F3%90%E7%2F%5Ev%5EY%5E%FB%BD%1EO%B3%9C%26%9E%D60m%C8%DB%C4%5B%E0%BD%CB%7B%60%3A%3E%3De%FA%CE%E9%03%3E%C6%3E%02%9Fz%9F%87%BE%A6%BE%22%DF%3D%BE%23~%D6~%99~%07%FC%9E%FB%3B%FA%CB%FD%8F%F8%BF%E1y%F2%16%F1N%05%60%01%C1%01%E5%01%BD%81%1A%81%B3%03k%03%1F%04%99%04%A5%075%05%8D%05%BB%06%2F%0C%3E%15B%0C%09%0DY%1Fr%93o%C0%17%F2%1B%F9c3%DCg%2C%9A%D1%15%CA%08%9D%15Z%1B%FA0%CC%26L%1E%D6%11%8E%86%CF%08%DF%10~o%A6%F9L%E9%CC%B6%08%88%E0Gl%88%B8%1Fi%19%99%17%F9%7D%14)*2%AA.%EAQ%B4Stqt%F7%2C%D6%AC%E4Y%FBg%BD%8E%F1%8F%A9%8C%B9%3B%DBj%B6rvg%ACjlRlc%EC%9B%B8%80%B8%AA%B8%81x%87%F8E%F1%97%12t%13%24%09%ED%89%E4%C4%D8%C4%3D%89%E3s%02%E7l%9A3%9C%E4%9AT%96tc%AE%E5%DC%A2%B9%17%E6%E9%CE%CB%9Ew%3CY5Y%90%7C8%85%98%12%97%B2%3F%E5%83%20BP%2F%18O%E5%A7nM%1D%13%F2%84%9B%85OE%BE%A2%8D%A2Q%B1%B7%B8J%3C%92%E6%9DV%95%F68%DD%3B%7DC%FAh%86OFu%C63%09OR%2By%91%19%92%B9%23%F3MVD%D6%DE%AC%CF%D9q%D9-9%94%9C%94%9C%A3R%0Di%96%B4%2B%D70%B7(%B7Of%2B%2B%93%0D%E4y%E6m%CA%1B%93%87%CA%F7%E4%23%F9s%F3%DB%15l%85L%D1%A3%B4R%AEP%0E%16L%2F%A8%2Bx%5B%18%5Bx%B8H%BDHZ%D43%DFf%FE%EA%F9%23%0B%82%16%7C%BD%90%B0P%B8%B0%B3%D8%B8xY%F1%E0%22%BFE%BB%16%23%8BS%17w.1%5DR%BAdxi%F0%D2%7D%CBh%CB%B2%96%FDP%E2XRU%F2jy%DC%F2%8ER%83%D2%A5%A5C%2B%82W4%95%A9%94%C9%CBn%AE%F4Z%B9c%15a%95dU%EFj%97%D5%5BV%7F*%17%95_%ACp%AC%A8%AE%F8%B0F%B8%E6%E2WN_%D5%7C%F5ym%DA%DA%DEJ%B7%CA%ED%EBH%EB%A4%EBn%AC%F7Y%BF%AFJ%BDjA%D5%D0%86%F0%0D%AD%1B%F1%8D%E5%1B_mJ%DEt%A1zj%F5%8E%CD%B4%CD%CA%CD%035a5%ED%5B%CC%B6%AC%DB%F2%A16%A3%F6z%9D%7F%5D%CBV%FD%AD%AB%B7%BE%D9%26%DA%D6%BF%DDw%7B%F3%0E%83%1D%15%3B%DE%EF%94%EC%BC%B5%2BxWk%BDE%7D%F5n%D2%EE%82%DD%8F%1Ab%1B%BA%BF%E6~%DD%B8GwO%C5%9E%8F%7B%A5%7B%07%F6E%EF%EBjtol%DC%AF%BF%BF%B2%09mR6%8D%1EH%3Ap%E5%9B%80o%DA%9B%ED%9Aw%B5pZ*%0E%C2A%E5%C1'%DF%A6%7C%7B%E3P%E8%A1%CE%C3%DC%C3%CD%DF%99%7F%B7%F5%08%EBHy%2B%D2%3A%BFu%AC-%A3m%A0%3D%A1%BD%EF%E8%8C%A3%9D%1D%5E%1DG%BE%B7%FF~%EF1%E3cu%C75%8FW%9E%A0%9D(%3D%F1%F9%E4%82%93%E3%A7d%A7%9E%9DN%3F%3D%D4%99%DCy%F7L%FC%99k%5DQ%5D%BDgC%CF%9E%3F%17t%EEL%B7_%F7%C9%F3%DE%E7%8F%5D%F0%BCp%F4%22%F7b%DB%25%B7K%AD%3D%AE%3DG~p%FD%E1H%AF%5Bo%EBe%F7%CB%EDW%3C%AEt%F4M%EB%3B%D1%EF%D3%7F%FAj%C0%D5s%D7%F8%D7.%5D%9Fy%BD%EF%C6%EC%1B%B7n%26%DD%1C%B8%25%BA%F5%F8v%F6%ED%17w%0A%EEL%DC%5Dz%8Fx%AF%FC%BE%DA%FD%EA%07%FA%0F%EA%7F%B4%FE%B1e%C0m%E0%F8%60%C0%60%CF%C3Y%0F%EF%0E%09%87%9E%FE%94%FF%D3%87%E1%D2G%CCG%D5%23F%23%8D%8F%9D%1F%1F%1B%0D%1A%BD%F2d%CE%93%E1%A7%B2%A7%13%CF%CA~V%FFy%EBs%AB%E7%DF%FD%E2%FBK%CFX%FC%D8%F0%0B%F9%8B%CF%BF%AEy%A9%F3r%EF%AB%A9%AF%3A%C7%23%C7%1F%BC%CEy%3D%F1%A6%FC%AD%CE%DB%7D%EF%B8%EF%BA%DF%C7%BD%1F%99(%FC%40%FEP%F3%D1%FAc%C7%A7%D0O%F7%3E%E7%7C%FE%FC%2F%F7%84%F3%FB%25%D2%9F3%00%00%00%04gAMA%00%00%B1%8E%7C%FBQ%93%00%00%00%20cHRM%00%00z%25%00%00%80%83%00%00%F9%FF%00%00%80%E9%00%00u0%00%00%EA%60%00%00%3A%98%00%00%17o%92_%C5F%00%00%06%9EIDATx%DA%BC%98%5Bl%1CW%19%C7%7Fs%D9Y%EF%3A%5E%C7%5D%3Bv%E3%CA6i%5D%DB%12%AEUPPH%147q%A5%60%11%9A%0A%C4%25%22%0F%20*%1A%A9RU%9E%40%81'%8AP%C4%03HI%80'%0AB%84%8B%A0*j%95%AAMi%AB%B6i%AD%A8%C46m%D3%C4%C6n%93%F5%25%F6%EE%DA%BB%DE%FB%EC%EC%9C9%3C%F8%2C%0C%5B%DF%12%1A%8E%F4%C9%3B%B3g%E7%FC%E6%FF%FF%CEw%CE%B1%26%A5D%D34%D6iM%CD%E1%F0%CF%3F%DB%DE%BEW%08Q%9E%CD%E7%93%D7VV%26%F3%8Es%11%18%01%26%00o%BD%1FK)%B9%D9%A6m%02t%EC%C7%FB%F6%9D%FD%FE%FE%FD%E08Tl%9B%85R%89%F1t%9A%97%AF_%2F%BF2%3B%3Bzuy%F9%AF%C0%D3%C0%F5%FF%07%D0%D1_%0D%0F%FF%F13%8D%8DD%82A%3A%1B%1BAJ%D040Ml%C7%E1B%3C%CEo%AF%5C%C9%3C%3B%3D%FDL%DEqN%03%FF%B8%9D%40%9D%C3%9D%9Do%3F%F5%C0%03%3B.%DE%B8%81%B4%2C%3E%19%8Dro%24%82%26%E5*%9Ca%80e1%95N%F3%D3%B1%B1%F2S%EF%BDw%C6%F5%BC'%81%DC%ED%00%028%FA%8D%DE%DE%DF%3D%D6%DBk%26%8AE%16l%1Bi%18%F4F%A3%F4D%22%EC%08%85V%E1%02%01%08%06ysv%96c%CF%3F%FF%C6L6%FB%1D)%E5%F8%CD%02%99%5B%E8%F3%9A%19%0AeZ%A3%D1h%2C%9B%E5%13%DB%B6%A1%037R)%A6%92IL%D3%A45%1CfG%7D%3D%11%C3%60ws3%C7%07%06%06%7Fp%E1%C27%81%DB%024%7C%A8%BB%3B%DA%19%0C%12%0B%87%99)%14%D0%80%88e%11%AD%AB%C3%D4uR%A5%121%DB%26bY%BC%9BH%F0%87%F7%DF%8F%01%2Fp%0BmS%A0%3B%EA%EB%1F%3C%D0%D2%C2%FC%CA%0A%12%B8%AB%BE%9E%8A%E7Qp%5D%96m%9B%8A%944Y%163%C5%22%AF%2C.r~ffl%3A%93y%14%18%BD%1D%40%DB%07%3B%3A%0EF%83A%26l%1B%C7%F3%D04%0DC%D3%D8nYH%CB%C2%D044M%E3%E4%5Bo%BD%3D%97%CF%3F%01%BC%03%94%B8%C5%A6o%F2%FD%DE%83%9D%9D%EDF%B9L%CEuWg%01%20%01!%25BJB%86%C1%D8%F22s%F9%FCY%E0%E2%FF%02%B3)P%9Di%3Ex%A8%A3%83d%3E%8F-%C4%9A%7D%02%86%C1%8B%B3%B3Y%E0%1C%1FC3%D7%AB%AE%9A%A6%05%EF%DB%B9s%A8%AB%BE%9EX%3A%8D%AB%EC%F2%B7%A0%AE%B3P%2C%F2%E6%E2%E2KR%CAk%FF%AE%25%AB%FD%B4%0D%CA%89%F4%89%BD1%90%A6i%9F%B6%02%81c%9D--%9Fz%B4%BF%FF%3E%CBq%C88%0E%B2FN%09%D4%07%02%BC8%3FO%DEq%FER%85%F0%0DV%1B%D2%07%20k%1E%B5%26%90%19%B2%AC%9F%1C%1F%1C%7C%E2%F8%FD%F7%1B%ED%40%C0qH%2C-QZ%C3%AE%EA%08%2F%CF%CF%CF%00%2F%A9%5B%D540%D4%B5%E1%03%C2%07%25%D5%A2%FC%91%CF%A6%2F%17~%F6%A7G%1Ey%FCHO%0F%24%12%E4%D2i2%B6M%C1u%D7%B4%2Bl%9A%5CN%A5%F8%7B%22qVJ%B9%A2%BE%AF%C2%04T%98*t%DF%C0%1E%20j%A2z%DF%AB%02%7D%F9%C9%87%1Ez%FCHw7%CC%CD%81%EB%D2%60%9A%E4%3D%8F%D9B%01%C7%F3%B0%0C%E3%BFL74%8D%0F%F2y%A4%94Z%0D%8C%05%84%800P%A7%AE%ABn%0B%C0%05*%EA%AF%A3%A2R%BDg%02%B456~%F5%F8%C0%00%C4%E3%E08%20%04R%08%EE%0C%850%A2Q%AEg2%AC8%0E%AE%E7a%E8%3A%3A0_(%F0%ED%BE%3E%80%13%3F%BAt%E92%F0g%A5F%1D%B0%5DE%83%BA%AE*%24%D4%C0%0E%60%AB(%A9(%02e%13%60%7B%5D%5Dk%A8%5C%86b%11*%15%1C%D7%C5%16%02!%04A%5D%A7%AF%A9%89%A2%EB%92w%1C%0AB%20%3C%8F%5D%91%08%A6%E7%F1%F5%5D%BB8%17%8B%FDp%3C%99%FC%9Bz%EBF%20%0A%B4%00%11%9FBU%CB%AA%CA%94%15H%1E%C8%AA%3E%AB%96M%C6%E3%AF%8E%C4b%83C--dK%25l!(%0A%81%E3yT%84%C0U%3B%82%AA'%E8%3A%E9r%99k%8EC%24%10%E0HW%D7%3D%E3%C9%E40%F0%3A%D0%044%2B%A8%B0o%E2H_%FET%80%A0%825%D5%FD2P4U%CF_%3Cv%FE%FC%17%CF%1D%3E%3C%D0%AC%EB%2C%96JT%3C%0F%5B%08*%9E%87%04%5C)%91%D5%F8O%D1%22'%25%9D%E10%86%AE%1F%10%9E7%A1%94%B9C%C1%04%7C%EA%F8%93%BA%E2S%C9%F6%E5%91%A8%26%DB%D2d*u%F4%D0s%CFM%BC%BA%B0%40%9DaPV%40%AEZ%22P%20%9EZ6*%9EG%C9%F3%C8%BB.I%DB%C6%93%D2%00%EE%02ZU%EE%D4Z%E5%00%05%20%05%C4%819%60%16%B8%01%24%81%0C%60%D7n%D0%DA%82%A6y%FA%5B%3D%3D_%F9%DA%DDw%D3%A0i%A4K%25%84i%E2x%1E%AE%E7%AD%DA%A8%A2%2C%04!%C3%E0%F5%C5E~%3D1%F1K%E0%5D%1F%88%EE%CB%19%1B%C8)%98e5x%C1%A7%8C%ABB%7Cd%C7%A8%AE%3F%DF%16%0E%7F%EFK%DD%DD%7B%87%BA%BA%CC%90m%E3i%1A9%C7!_%A9P%16%82%8A%B2%2Fa%DB%FCfrr!Q*%9DVIj%A8GU%AD%C9%03K%40%C2%A7D%C9W%83%FC%15%7C%CD%A5%03%E0%85%C5bq%FCj%5B%DBx%FF%C3%0F%B7%9E9q%22%13%12%C2hoh%D8V%A7%A6%7D%CEuY(%95%BC%89T%EA%9FE%D7%7DV%BD%ADQ%B3L8%0A%E2%9A%B2%A9%A0%20%E5Z%EB%98%94r%C3%FD%D0%81%7D%BBw%B7%5E%1E%1D%E5J%3C%FE%0Cputi%E9%5EU%F4%02%CA%86%25%60Q%A9Q%BB%92VgNJ%A9%93U%B6lx~%5B%0FHkjj%3A%DC%DD%DD%CD%A9S%A7%0A%C0%A4z%F8%15_%A2j%EB%AC%E2%FE%24%CE%02i%9FE%9B%1E%26%D7%DC~h%9A%D6%B8g%CF%9E%83%0D%0D%0DLMM%5D%02%A6T%5D%09%AD'%B7%0F%C8U%00%CB%CA%AA%05%A5%A6%DC%CAAr%3D%85%0E%0C%0F%0F%EF%9C%9E%9E%26%97%CB%3D%0D%7C%A8%94%A9%E6GY%A9%A0%FB%94%AAZ%B4%A2%F2%25%AE%3E%97%B7%0A%B3.Pss%F3%17%FA%FB%FB9y%F2d%1A%18%03%EET%955%09%C4T%EE%C8%9A%24%AE%5D%12*7%03%B2%D1%2C%0B%0F%0D%0D%ED%CFf%B3%8C%8C%8C%BC%03%DC%A3%DE%FE%03e%5DF%0DV%BB%CF%E1V%00%B6%F2%1F%8ApGG%C7%87%7D%7D%7D%128%03%7CN%E5%8E%BE%DE%EF%3F%CEX%EB(%DD%00%FC%5E%25%E4w%A5%94%E5%DA%CD%D9%AD%9C%D9%B7%DA%FE5%00%9F%1B%AC%F1%FAw%1F%17%00%00%00%00IEND%AEB%60%82"});
  580.         document.body.appendChild(img);
  581.         
  582.         // Append a message to highlight the notifications
  583.         if (options && options.get('highlight_pin_notifications')) {
  584.             $$('presence_menu_content_wrapper',function(){
  585.                 insertFirst( this, message('Your notifications will now be pinned up here for easy reading and access. This can be turned off in the Options',function(){ options.set('highlight_pin_notifications',false); options.save();}) );
  586.             },notifications);
  587.         }
  588.         
  589.         onDefined(unsafeWindow,'presenceNotifications', function(o) {
  590.             unsafeWindow.presenceNotifications.loadTab();
  591.         
  592.             // Highlight the new notifications in a more visible color
  593.             var unread = document.getElementsByClassName('notif_unread');
  594.             if (unread) {
  595.                 for (var i=0; i<unread.length; i++) {
  596.                     unread[i].style.backgroundColor='yellow';
  597.                 }
  598.                 setTimeout( function() {
  599.                     if (unsafeWindow.presenceNotifications && unsafeWindow.presenceNotifications.markRead) {
  600.                         unsafeWindow.presenceNotifications.markRead();
  601.                     }
  602.                 },5000);
  603.             }
  604.         });
  605.     });
  606. }
  607.  
  608. // Last seen article ID
  609. var last_seen_fbid = options.get('last_seen_fbid') || 0;
  610.  
  611. function hideClass(cn) {
  612.     $$(cn, function() { this.style.display='none'; } );
  613. }
  614. function unhideClass(cn) {
  615.     $$(cn, function() { this.style.display='block'; } );
  616. }
  617. function getStream() {
  618.     var els = document.getElementsByClassName('UIIntentionalStream_Content');
  619.     if (els) { return els[0]; }
  620. }
  621. var comment_expire_time = 1000 * 60 * 60 * 24 * 7; // ONE WEEK
  622. function markRead() {
  623.     // Find the fbid of the top-most story currently displayed
  624.     var el = getStream();
  625.     if (el && el.firstChild) {
  626.         var first = el.firstChild;
  627.         var fbid = getStoryProperty(first,'fbid');
  628.         options.set('last_seen_fbid',fbid);
  629.         addClass(first,"last_read");
  630.         
  631.         // Mark the entire stream as read
  632.         addClass(el,"all_read");
  633.     }
  634.     
  635.     // Prune the old comment counts so the prefs don't get huge
  636.     var t = time();
  637.     var story_data = options.get('story_data');
  638.     if (DEBUG) { console.log("Pruning old story data from prefs"); }
  639.     if (DEBUG) { console.log(story_data); }
  640.     if (story_data) {
  641.         for (var s in story_data) {
  642.             var story = story_data[s];
  643.             var cc = story.cc;
  644.             if (cc && cc.t) {
  645.                 if (DEBUG) { console.log("Stored time="+cc.t+", diff="+(t-cc.t)); }
  646.                 // This is the new format
  647.                 if (t-cc.t > comment_expire_time) {
  648.                     if (DEBUG) { console.log("expiring comment storage!"); }
  649.                     delete story_data[s];
  650.                 }
  651.             }
  652.             else {
  653.                 // Old format, get rid of it
  654.                 if (DEBUG) { console.log("Deleting old story data"); }
  655.                 delete story_data[s];
  656.             }
  657.         }
  658.         options.set('story_data',story_data);
  659.         if (DEBUG) { console.log(story_data); }
  660.     }
  661.     
  662.     options.save();
  663. }
  664.  
  665. function showAll() {
  666.     var el = getStream();
  667.     if (el) {
  668.         addClass(el,"show_all");
  669.         document.getElementById('better_fb_hide_read').style.display='';
  670.         document.getElementById('better_fb_show_read').style.display='none';
  671.     }
  672. }
  673.  
  674. function hideRead() {
  675.     var el = getStream();
  676.     if (el) {
  677.         removeClass(el,"show_all");
  678.         document.getElementById('better_fb_hide_read').style.display='none';
  679.         document.getElementById('better_fb_show_read').style.display='';
  680.     }
  681. }
  682.  
  683. function button(value,onclick,id) {
  684.     var button = el( 'input','UIButton_Text',{type:'button','value':value},{click:onclick} );
  685.     var span = el('span','UIButton UIButton_Blue UIFormButton UIButton_better_fb',{'id':id});
  686.     span.appendChild(button);
  687.     return span;
  688. }
  689.  
  690. function message(str,okFunc) {
  691.     var msg = el('div','better_fb_message');
  692.     msg.innerHTML = str;
  693.     okFunc = okFunc || (function(){});
  694.     var ok = el('div','better_fb_close',{innerHTML:'OK'},{click:function() { okFunc(); this.parentNode.style.display='none'; } } );
  695.     msg.appendChild(ok);
  696.     return msg;
  697. }
  698.  
  699. function createSidebarSection(o) {
  700.     var d = el('div');
  701.     d.innerHTML = _template( (<><![CDATA[
  702.     <div>
  703.         <div class="UIHomeBox UITitledBox">
  704.             <div class="UITitledBox_Top clearfix">
  705.                 <div class="UITitledBox_TitleBar">
  706.                     <div class="UITitle UITitle_h5" id="%id%_title">%title%</div>
  707.                 </div>
  708.             </div>
  709.             <div class="UITitledBox_Content" id="%id%_notification"></div>
  710.             <div class="UITitledBox_Content" id="%id%">
  711.                 %content%
  712.             </div>
  713.         </div>
  714.     </div>
  715.     ]]></>).toString(), o );
  716.     return d;
  717. }
  718.  
  719. function ago(when,now) {
  720.     var diff = Math.floor((now-when)/1000/60);
  721.     if (diff<60) {
  722.         return diff+" minutes ago";
  723.     }
  724.     diff = Math.floor(diff/60);
  725.     if (diff<24) {
  726.         return diff+" hours ago";
  727.     }
  728.     diff = Math.floor(diff/24);
  729.     return diff+" days ago";
  730. }
  731.  
  732. // Attach new Sidebars!
  733. onIdLoad('home_sidebar',function(o) {
  734.  
  735.     if (options && options.get('show_friend_tracker')) {
  736.         // A "Friend Tracker" sidebar will notify if anyone unfriended you
  737.         // Some idesa taken from http://userscripts.org/scripts/review/40027
  738.         insertAtPosition( o, createSidebarSection({ title:'Friend Tracker',content:'Loading...',id:'better_fb_friends_pagelet'} ), 3 );
  739.         
  740.         // Highlight the added sidebar section
  741.         if (options && options.get('highlight_friend_tracker')) {
  742.             insertFirst( document.getElementById('better_fb_friends_pagelet_notification'), message('If anyone unfriends you, you will see it here! This sidebar section can be disabled in the options.',function(){ options.set('highlight_friend_tracker',false); options.save();}) );
  743.         }
  744.         
  745.         var t = time();
  746.         GM_xmlhttpRequest({
  747.             method: 'get',
  748.             headers: { 'Content-type': 'application/x-www-form-urlencoded' },
  749.             url: "http://www.facebook.com/ajax/typeahead_search.php?__a=1&time="+t,
  750.             onload: function(result) {
  751.                 var friends = options.get('friend_tracker') || {'friends':{},'unfriended':{}};
  752.                 
  753.                 // First version was messed up, so need to fix it via a hack
  754.                 if (!friends.fixed) {
  755.                     friends = {'friends':{},'unfriended':{}};
  756.                     friends.fixed = true;
  757.                 }
  758.                 
  759.                 var old_friends = friends.friends;
  760.                 var unfriended = friends.unfriended;
  761.                 var count = 0;
  762.                 var friend_list = JSON.parse(result.responseText.substring(9)).payload.entries;
  763.                 var current_friends = {};
  764.                 for (i=0;i<friend_list.length;i++) {
  765.                     if (friend_list[i].ty=="u") { // only users
  766.                         count++;
  767.                         var id = friend_list[i].i;
  768.                         var name = friend_list[i].t;
  769.                         var f = {'name':name,'added':t};
  770.                         // User wasn't my friend before
  771.                         if (typeof old_friends[id]=="undefined") {
  772.                             // Add them into the list of my friends!
  773.                             old_friends[id] = f;
  774.                         }
  775.                         // Maintain a list of all current friends
  776.                         current_friends[id] = f;
  777.                     }
  778.                 }
  779.                 
  780.                 // Now loop through all my old_friends, to see if they no longer exist in current_friends
  781.                 var html = "";
  782.                 for (var id in old_friends) {
  783.                     if (typeof current_friends[id]=="undefined") {
  784.                         // They unfriended you! Bastards!
  785.                         unfriended[id] = old_friends[id];
  786.                         unfriended[id].deleted = t;
  787.                         delete old_friends[id];
  788.                     }
  789.                 }
  790.                 for (var id in unfriended) {
  791.                     var f = unfriended[id];
  792.                     if (t-f.deleted > 1000*60*60*24*7) { // Only show unfriends for 1 week
  793.                         delete unfriended[id];
  794.                     }
  795.                     else {
  796.                         html += '   <a href="/profile.php?id='+id+'">'+f.name+'</a> <span class="GenericStory_Time">'+ago(f.deleted,t)+'</span><br>';
  797.                     }
  798.                 }
  799.  
  800.                 if (html) {
  801.                     html = "You've been unfriended by:<br>"+html;
  802.                 }
  803.                 else {
  804.                     html = "No activity";
  805.                 }
  806.                 document.getElementById('better_fb_friends_pagelet').innerHTML = html;
  807.                 
  808.                 // Update total friend count
  809.                 document.getElementById('better_fb_friends_pagelet_title').innerHTML += " ("+count+" friends)";
  810.                 
  811.                 // Finally, save the details!
  812.                 options.set('friend_tracker',friends);
  813.                 options.save();
  814.             }
  815.         });
  816.     }
  817.  
  818.     if (options && options.get('show_group_activity')) {    
  819.         // A "Groups Activity" sidebar will alert you to groups activity
  820.         insertAtPosition( o, createSidebarSection({ title:'My Group Activity',content:'Loading...',id:'better_fb_groups_pagelet'} ), 4 );
  821.         // Highlight the added sidebar section
  822.         if (options && options.get('highlight_group_activity')) {
  823.             insertFirst( document.getElementById('better_fb_groups_pagelet_notification'), message('This new sidebar will alert you to new posts, members, etc in groups that you are a member of.',function(){ options.set('highlight_group_activity',false); options.save();}) );
  824.         }
  825.         var t = time();
  826.         var i = document.createElement("iframe");
  827.         i.src = "http://www.facebook.com/groups.php";
  828.         i.style.display="none";
  829.         i.addEventListener('load',function(e) {
  830.             var d = e.target.contentDocument;
  831.             var html = "";
  832.             var td = d.getElementsByClassName("group_list")[1];
  833.             $$("group",function() {
  834.                 var updates = this.getElementsByClassName('updates');
  835.                 if (updates && updates.length>0) {
  836.                     var update = updates[0];
  837.                     var h4 = update.parentNode.parentNode.parentNode.firstChild;
  838.                     html += '<h4 class="bf_group_h4">'+h4.innerHTML+'</h4><span class="bf_group_update">'+update.innerHTML+'</span>';
  839.                 }
  840.             },td);
  841.             if (!html) {
  842.                 html = "No activity";
  843.             }
  844.             document.getElementById('better_fb_groups_pagelet').innerHTML = html;
  845.         },false);
  846.         var pagelet = document.getElementById('better_fb_groups_pagelet');
  847.         if (pagelet) {
  848.             pagelet.appendChild(i);
  849.         }
  850.     }
  851. });
  852.  
  853. // Attach a new control panel DIV
  854. function createControlPanel() {
  855.     var cp = el('fieldset','better_fb_cp',{id:'better_fb_cp'});
  856.     cp.appendChild( el('legend',null,null,null,'Better Facebook!') );
  857.     
  858.     cp.appendChild( button('Mark All Read',markRead,'better_fb_mark_read') );
  859.     cp.appendChild( button('Show All',showAll,'better_fb_show_read') );
  860.     cp.appendChild( button('Hide Read',hideRead,'better_fb_hide_read') );
  861.     cp.appendChild( button('Reload',function(){window.location.href='http://www.facebook.com/home.php?filter=nf';},'better_fb_reload') );
  862.     cp.appendChild( button('Options',better_fb_options,'better_fb_options') );
  863.  
  864.     if (options && options.get('highlight_cp')) {
  865.         var msg = (<><![CDATA[
  866.             <p>Better Facebook has added this control panel!</p>
  867.             <p style="font-weight:bold;">Read these notes before continuing, so you know what Better Facebook can do for you!</p>
  868.             <p>Click 'Mark All Read' once you have read every story on this page. The stories will then be hidden the next time you view the page. But don't worry - if any new comments are added they will show back up! This will let you keep track of conversations that you otherwise might have missed.</p>
  869.             <p>You can always go back and see all the hidden stories by clicking 'Show All', then hide them again by clicking 'Hide Read'.</p>
  870.             <p>You can change options for your Facebook page by clicking the 'Options' button!</p>
  871.             <p><u>This functionality should only be used on the Live Feed!</u> Occasionally this control panel will be visible on the News Feed, but it should hide itself. If you navigate to other pages (like Inbox, etc) then back to home by clicking the facebook logo or Home link, this control panel may disappear. It will be visible again when you load the page again.</p>
  872.             <p>Enjoy a Better Facebook!</p>
  873.         ]]></>).toString()
  874.         cp.appendChild( message(msg, function() { options.set('highlight_cp',false); options.save(); }) );
  875.     }
  876.     
  877.     document.getElementById('home_stream').insertBefore( cp, document.getElementById('pagelet_intentional_stream') );
  878.  
  879.     // By default, hide the 'hide read' button
  880.     document.getElementById('better_fb_hide_read').style.display='none';
  881. }
  882. onIdLoad('home_stream',createControlPanel);
  883.  
  884. function better_fb_options() {
  885.     options.displayOptions();
  886. }
  887.  
  888. // Do an initial fix in case there is static content
  889. var content = document.getElementsByClassName('UIIntentionalStream_Content');
  890. if (content && content.length) {
  891. //    log("static content!");
  892.     content = content[0];
  893.     fixStories(content.getElementsByClassName('GenericStory'));
  894. }
  895.  
  896. // Fix anything added later
  897. document.addEventListener("DOMNodeInserted", handleDomInsertion, false);
  898.  
  899. // ==================================================================
  900. // "Reusable" Options interface
  901. // ==================================================================
  902. function GM_options(key) {
  903.     this.optionsDiv = null;
  904.     this.options = [];
  905.     this.optionsObj = {};
  906.     this.key = key;
  907.     
  908.     this.prefs = {};
  909.     var storedPrefs = getValue('prefs',null);
  910.     if (storedPrefs && storedPrefs!=null && storedPrefs!='' && storedPrefs!="null") {
  911.         this.prefs = JSON.parse(storedPrefs);
  912.     }
  913.     
  914.     this.get = this.set = function(name,val) {
  915.         var undef;
  916.         var parts = name.split(".");
  917.         var prop = parts[parts.length-1];
  918.         var o = this.prefs;
  919.         var isGet = arguments.length==1;
  920.         var option = this.optionsObj[name];
  921.         var def = option?option['default']:undef;
  922.  
  923.         for (var i=0; i<parts.length-1; i++) {
  924.             var part = parts[i];
  925.             if (typeof o[part]=="undefined") {
  926.                 if (isGet) { 
  927.                     return def; 
  928.                 }
  929.                 o[part] = {};
  930.             }
  931.             o = o[part];
  932.         }
  933.         if (!isGet) {
  934.             o[prop] = val;
  935.             return val;
  936.         }
  937.         
  938.         if (typeof o[prop]!="undefined") {
  939.             return o[prop];
  940.         }
  941.         return def;
  942.     }
  943.  
  944.     this.save = function() {
  945.         var json = JSON.stringify(this.prefs);
  946.         setValue("prefs",json);
  947.     }
  948.     
  949.     this.addOption = function(opt) {
  950.         this.options.push( opt );
  951.         this.optionsObj[opt.name] = opt;
  952.     };
  953.     this.addHtml = function(html) {
  954.         this.options.push( {'type':'html', 'value':html } );
  955.     };
  956.     this.addFunction = function(func) {
  957.         this.options.push( {'type':'function', 'value':func } );
  958.     }
  959.     this.renderOption = function(opt) {
  960.         opt.value = this.get(opt.name);
  961.         opt.onchange = opt.onchange || "";
  962.         var input = '';
  963.         // CHECKBOX Option
  964.         if (opt.type=="checkbox") {
  965.             opt.checked = (opt.value?"checked":"");
  966.             input = _template('<input type="checkbox" name="%name%" onclick="%onchange%" %checked%>', opt);
  967.         }
  968.         // TEXTAREA option
  969.         else if (opt.type=='textarea') {
  970.             input = _template('<textarea name="%name%" nowrap class="textarea" onchange="%onchange%" rows="%rows%" cols="%cols%">%value%</textarea>', opt);
  971.         }
  972.         // INPUT option
  973.         else {
  974.             input = _template('<input name="%name%" class="text" onchange="%onchange%" value="%value%" size="%size%">', opt);
  975.         }
  976.         return input;
  977.     };
  978.     this.render = function() {
  979.         var content = "";
  980.         var self = this;
  981.         this.options.forEach(function(opt,i) {
  982.             if (opt.type=='html') {
  983.                 content += '<tr><td colspan="2" class="html">'+opt.value+'</td></tr>';
  984.             }
  985.             else if (opt.type=='function') {
  986.                 content += opt.value(opt,i);
  987.             }
  988.             else if (opt.type!='hidden') {
  989.                 var input = self.renderOption(opt);
  990.                 content += '<tr><td class="label">' + opt.description + '</td><td class="input">' + input + '</td></tr>';
  991.             }
  992.         });
  993.         return content;
  994.     };
  995.     this.displayOptions = function() {
  996.         var doc = document.wrappedJSObject;
  997.     
  998.         var optionsContent = this.render();
  999.         var optionsWrapper = _template( (<><![CDATA[
  1000.             <form name="%key%">
  1001.                 <div class="GM_options_header">
  1002.                     <div class="GM_options_buttons">
  1003.                         <input type="button" name="GM_options_save" value="Save">
  1004.                         <input type="button" name="GM_options_cancel" value="Cancel">
  1005.                     </div>
  1006.                     <a href="http://betterfacebook.net/"><img src="http://s3.amazonaws.com/uso_ss/icon/61761/thumb.png?1257973686" width="120" height="90" align="left" style="margin-right:20px;" border="0"></a>
  1007.                     <a href="http://userscripts.org/scripts/show/71761" target="_blank">Script Home</a> | 
  1008.                     <a href="http://userscripts.org/scripts/reviews/71761" target="_blank">Review This Script</a> | 
  1009.                     <a href="http://userscripts.org/scripts/discuss/71761" target="_blank">Problems? Suggestions? Discuss!</a> |
  1010.                     <a href="mailto:gadgets@mattkruse.com">Email me feedback!</a>
  1011.                     <br>
  1012.                     <a href="http://www.facebook.com/pages/Better-Face-Book/174424289341" target="_blank"><img border="0" src="data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%BD%00%00%00%19%08%02%00%00%00%EB8%B8%9F%00%00%05%BBIDATx%DA%ED%5B%5DL%1CU%14%FE%AE%F2%E8%03%2F%05j%C4%B6%C6%1AM%D8%D6%98%DD5%D5%F0R%B6%2C%B4%E9%13%D0%A6%96XZjj%B6%5B%81%0Eu!VJ%B5%B1n%BA%DBe%5B%C4%D0%D2%40%7F%C4d%81%87j%D3%A5%2C%F4%C1%1A%25Lc%16L%D4%F4%C1%96%18%97%C5%A4%C6'1%26%CE%F1%E1%EE%CE%FEpg%3A%A0%A65%DC%93%7D%98%B9s~%BE%7B%E6%9Bs%EE%9D%01%B6%B0%B0%00)R%96(%05%00%C2%E1%B0L%84%14%EB%D2%D4%D4T%C0%8F%7C%3E%9FL%87%14%2B%E2%F7%FBS%F5%06%C0%EF%B2%5BIYj%9F%02%00%8D%ACh%BF%E3y%0D%60%0C8%D1%F3%89%CC%9D%E4%0D%08%F9%BC%F9%EE%CDM%60%60%00%11%C0%00%02%18v%A7%94%05%FARV%24o%16%D1%E0%85%8F%BF%FE%C1%B3%E9%D9%AD%EB%18c%05%AB_%FA%EB%B7%7B%B4%F0%EB%1F%B3%F7%EFW%1E%EF%0B%9D%7DO%D2F%F2FL%1C%E0%F9%8F%BE%FA%DE%F3%CA%FAm%EB%F4%9AD%00%98%5E%85%A4%ACx%DEh%D0%84%97%19%03ci%92%10%18%03%88%88%0C%F5%A5%3CLI%5Eo%DB%15%98r%B6%0E%FA%B7%96%FC%C7%E6z%9F%12%D4%8F%3B%DE%F2%E7%B6%3DC%20%C68%87%18%C0%AED%C6%FF%7C%E2%C96_%1B%D7)%2C%2C%F4%B5%89%F6%F0%D3%BD%15-%91%CC%A9%B3%F5%93%0F%ABK%1E%D9%8C%3F%2C%B4%FFn%5C%D2%9B%C7%B2%DA%C1R%CC%0B%F2l2%A49T%0E%E0%CE%B5%1F%19%03%E1n%FB%E3M%FA%FAx%A2%BFQW%3Bx%F0%A0x%89%CC%07w%9C%9E8%B0q%BA%B7%E2p%24p5%5E%7D%60%E3%A3%CA%9B%2C%B4F%09%F9%1F%C4%25%FC%23'K17%AC7%EB%CF%7C%91s%DE2%D2%E9%DD%0C%B0%CE%EE%F1%9CXd%C0N%D2%AF%CF%FD%7C%17%80%A3%B4%88%88%80%E9s.%25%FD%849%94%2B'%ABK%20%18D%B4%BD%3E%A8%E6h%25%A3%ED%F5A%D5%E1p%A8%AA%0A%00%3B%82A(%DC%CA%D8O%E6%D9Mfy%5Ct1%0B-%99%E9s%FF%3A%86%7C%2F9F%96L%2C%C6%15Nm%F1%08%F72%15%D8%5D%11%C86%16%A0%12%0Ers%22%22%8A%9Fs)%11%8C%8F%8F%1B%F1%E6%B14p%CD%E8W%D32R%DB2%A2%D70%06V%B1%F7%82k%EF%85%2C%EA%08%0D%09%00%22%8A%CBU%1FT%81%BA%9DU%C5%1A%CD%5DoW%22%0E%E5R%2C6%16%0B%D4A%0D%86%A3%09%8A%F7%BA%94%08%1C%87%2F%C7%C6b%B1K%CAZ%A2xo%7DPE%5D%20%16%1B%0B%D4A%0D%D6%F7%C6%D3%0E%D5%B5%3Bcc%97%15%07%10Q%26%9D%5C%01j%F0j%DC%C0%B9%8E%A7%D8%FDAl%2C%C6C8%A0%06%C3%D19!Z%97%CB%E5j%8F%26%0C%F4y%1A%CA%DF%CA%8E%AB%3B1%81mdb1%EE%E2%14%89'K%00%B8%DA%E5%94qB%8C%CA%0C*Q%BCW%89%00u%01%E1%9D%CD%5D%17%9B%BE%F7%3B%E6%DDL%00oS%C7%BC%9B%01%1C%EF%BE%99%5E83%B1-%07Q%7B%EA%C6%1B%1B0s%DE%7D%A4u%0B%9D%BAXzK%05%10%7C%7DK0%A5%A5%CE%CE%C7g%87%00%D4%EEp%17i%A4%A1%B8r%BF%7B%E6%BC%1B%40%AD%D3%A6ids%D6bhxhrz%FFv%9E%94%A7Vi%A4i%E9%C3%F4d%89(%F1%8D%C0%B9%A6%15g%8A%C1%E8%D1%3D!%FDI%86%96%F6%93%8F6%7DU%A0%CF%D5%D6%AC.%D2(%99%8E%AB%3B%99%99%1C%12%C0~%D9%CC%C4b%DC%F8d~%8A%92%A3G%05%93%DD%90%BA%F1%9AF%25%25k%005%93%DE%5CTN%0C%19f%18%B7%CE%B4%AAp%B4%5C%DCo3aE%81%85%96F%00%18%18%18%88%C0%F2%E9Af%EB%1B~PT%EA%00%D4%7B%89_J%01%C0%D1r%F1%84%3BS%AAg%FA2%2F%13%F3%3D%18%0D%1A%B7%E4%3C%E7%FAx%F2%C6%D1%3D!%95_%9D%E9s%1F%196%E8%E8%CB%D07%87mnb1%AEA%AC%FC%C9%CE'%1E%907%2B%83*%60%07%D4P%F7%E8%8B9%CE%C5%7D%0A%9A%D1%8F%81%1D%EF%BE%D9%D9%3D%01%02%03%3A%BB'%F4b%C3%A5%E3XG%F0tp%91!%87%A3%11%B4d%FC%96%0A%D8%CB7%DAV%3F%0D%40%0D%7D6%9D%A5is%D6%00%18%8ED%93%D0%08s%A3%7D%D1%22%3E2%15'h%D3S%C3%00j%9De%A9%B7G%20%40%E3s%25%10e%BE%8Fh%C5%22%E7%FAo%FE'5m2%97%B8%97%F2%23Dk%AA%9F%AD%86%3C%13%9B%01l%13%13%8Bq%17%A7%08%E2%C9fR%A4%03%10%A2%B2%99d%D8%FE%AA%F7P%B3%1DPCg%85%7C%C8%5D%17%1BW%A4O%FD%DB%01%EC%F2%7DN%20%5El%C6%B3%F6S%3D%3D%3D%00%3C%1EO%BE%07~6%EC%AB%E2O%8C%BD%D9%BB%A5%88%D0%18%ED%2F%7Dwo%D7%DBU%23H%0D%F7%BF_%D9%18%F5%A3%DA%D7%B5%A7%AA%8B%0F%0D%ECk%1Ch%9Em%E8J%D9%DA%9B%FB%1B%CB%88%E6%91%81%9An%C4%94%D5%A7%C8%26t%9E%EAS%B6%7D%FE%9Aa%DFH%A8%A1*d%B7%DBS~H%CBEK%99%3C%88%F5%B3%D42qu'e%22%D8%DF%9A%9AX%8C%5B%26H%91%60%B2%7COv%3B%ADV%E3o%2C%23%82%08%15L3%BC%AA%D2%DB%FCeC%D7m%13V%B0%85%85%85p8%DC%D0%D0%60%BE%EF%DA%DDvM%9Ff%F6%3E%5C%DF%8Dwtt%C8%17o%2BA%06%06%062%7F%7F%F3%C07%3DWNn%E3%07%81%40%C0%E3%F10%C6%B2M%0A%0B%0BI~yX%81%DF%19%AC%DFuEQ%C4%8Bg%C9%9B%95%F8%7DJ%DEu)%CB%E0%0DH~%A7%94%B2%8Cz%A3%C9z%23e%E9%BC%19%1C%1C%94%B9%90b%5D%98%FC%FF))%CB%90%BF%01%C9%2B%7F%1ACT0%C2%00%00%00%00IEND%AEB%60%82"></a>
  1013.                     <br>
  1014.                     <b>Support Development! Make a donation with PayPal: <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=9627367" target="_blank"><img border="0" src="https://www.paypal.com/en_US/i/btn/btn_donate_SM.gif"></a></b>
  1015.                     <br>
  1016.                     <br>
  1017.                     If you like Facebook, you might get a chuckle out of <b><a href="http://ObnoxiousFacebookHabits.com" target="_blank">ObnoxiousFacebookHabits.com</a></b>!
  1018. <!--                    
  1019.                     <div class="better_fb_message">
  1020.  
  1021.                     </div>
  1022. -->
  1023.                 </div>
  1024.                 <div class="GM_options_body">
  1025.                     <table class="GM_options" id="GM_options_%key%">
  1026.                         %options%
  1027.                     </table>
  1028.                 </div>
  1029.             </form>
  1030.         ]]></>).toString(), {'options':optionsContent,'key':this.key} );
  1031.         var div = this.optionsDiv;
  1032.         if (div==null) {
  1033.             this.optionsDiv = doc.createElement('div');
  1034.             div = this.optionsDiv;
  1035.             div.className = 'GM_options_wrapper '+this.key+'_wrapper';
  1036.             doc.body.appendChild(div);
  1037.         }
  1038.         div.innerHTML = optionsWrapper;
  1039.         var self = this;
  1040.         var f = doc.forms[this.key];
  1041.         f.GM_options_save.onclick = function() { self.saveOptions(); };
  1042.         f.GM_options_cancel.onclick = function() { self.cancelOptions(); };
  1043.         div.style.display="block";
  1044.     };
  1045.     this.hideOptions = function() {
  1046.         if (this.optionsDiv!=null) {
  1047.             this.optionsDiv.style.display='none';
  1048.         }
  1049.     };
  1050.     this.cancelOptions = function() {
  1051.         this.hideOptions();
  1052.     };
  1053.     this.saveOptions = function() {
  1054.         var doc = document.wrappedJSObject;
  1055.         var f = doc.forms[this.key];
  1056.         if (f && f.elements) {
  1057.             for (var i=0; i<f.elements.length; i++) {
  1058.                 var el = f.elements[i];
  1059.                 if (el.name && el.name.indexOf("GM_")!=0) {
  1060.                 if (el.type=="checkbox") {
  1061.                         this.set(el.name,el.checked);
  1062.                     }
  1063.                     else if (el.type=='text') {
  1064.                         this.set(el.name,el.value);
  1065.                     }
  1066.                     else if (el.type=='textarea') {
  1067.                         this.set(el.name,el.value);
  1068.                     }
  1069.                     else if (el.type=='select-one') {
  1070.                         this.set(el.name,el.options[el.selectedIndex].value);
  1071.                     }
  1072.                 }
  1073.             }
  1074.         }
  1075.         else {
  1076.             alert('Form not found!');
  1077.         }
  1078.         this.save();
  1079.         alert("Refresh the page to see your changes");
  1080.         this.hideOptions();
  1081.     };
  1082.  
  1083. }
  1084.  
  1085. // JSON 
  1086. // ----
  1087. (function() {
  1088. if(!this.JSON){this.JSON={};}
  1089. (function(){function f(n){return n<10?'0'+n:n;}
  1090. if(typeof Date.prototype.toJSON!=='function'){Date.prototype.toJSON=function(key){return isFinite(this.valueOf())?this.getUTCFullYear()+'-'+
  1091. f(this.getUTCMonth()+1)+'-'+
  1092. f(this.getUTCDate())+'T'+
  1093. f(this.getUTCHours())+':'+
  1094. f(this.getUTCMinutes())+':'+
  1095. f(this.getUTCSeconds())+'Z':null;};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf();};}
  1096. var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==='string'?c:'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4);})+'"':'"'+string+'"';}
  1097. function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==='object'&&typeof value.toJSON==='function'){value=value.toJSON(key);}
  1098. if(typeof rep==='function'){value=rep.call(holder,key,value);}
  1099. switch(typeof value){case'string':return quote(value);case'number':return isFinite(value)?String(value):'null';case'boolean':case'null':return String(value);case'object':if(!value){return'null';}
  1100. gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==='[object Array]'){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||'null';}
  1101. v=partial.length===0?'[]':gap?'[\n'+gap+
  1102. partial.join(',\n'+gap)+'\n'+
  1103. mind+']':'['+partial.join(',')+']';gap=mind;return v;}
  1104. if(rep&&typeof rep==='object'){length=rep.length;for(i=0;i<length;i+=1){k=rep[i];if(typeof k==='string'){v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}else{for(k in value){if(Object.hasOwnProperty.call(value,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}
  1105. v=partial.length===0?'{}':gap?'{\n'+gap+partial.join(',\n'+gap)+'\n'+
  1106. mind+'}':'{'+partial.join(',')+'}';gap=mind;return v;}}
  1107. if(typeof JSON.stringify!=='function'){JSON.stringify=function(value,replacer,space){var i;gap='';indent='';if(typeof space==='number'){for(i=0;i<space;i+=1){indent+=' ';}}else if(typeof space==='string'){indent=space;}
  1108. rep=replacer;if(replacer&&typeof replacer!=='function'&&(typeof replacer!=='object'||typeof replacer.length!=='number')){throw new Error('JSON.stringify');}
  1109. return str('',{'':value});};}
  1110. if(typeof JSON.parse!=='function'){JSON.parse=function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==='object'){for(k in value){if(Object.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v;}else{delete value[k];}}}}
  1111. return reviver.call(holder,key,value);}
  1112. cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return'\\u'+
  1113. ('0000'+a.charCodeAt(0).toString(16)).slice(-4);});}
  1114. if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,'@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']').replace(/(?:^|:|,)(?:\s*\[)+/g,''))){j=eval('('+text+')');return typeof reviver==='function'?walk({'':j},''):j;}
  1115. throw new SyntaxError('JSON.parse');};}}());
  1116. })();
  1117.